home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/kernel/blk_drv/floppy.c
- *
- * Copyright (C) 1993 Greg Harp
- * Atari Support by Björn Brauel
- *
- */
-
- #include <linux/config.h>
- #include <linux/sched.h>
- #include <linux/fs.h>
- #include <linux/fcntl.h>
- #include <linux/kernel.h>
- #include <linux/timer.h>
- #include <linux/fdreg.h>
- #include <linux/fd.h>
- #include <linux/errno.h>
- #include <linux/types.h>
- #include <linux/delay.h>
- #include <linux/interrupt.h>
- #include <linux/mm.h>
- #include <asm/system.h>
-
- #include <linux/atarihw.h>
- #include <linux/atariints.h>
-
- #define MAJOR_NR FLOPPY_MAJOR
- #include "blk.h"
-
- #undef __notneeded__
-
- /*
- * Defines
- */
- #define MAX_SECTORS 22
- #define RAW_BUF 900000
-
- /*
- * Error codes
- */
- #define FD_OK 0 /* operation succeeded */
- #define FD_ERROR -1 /* general error (seek, read, write, etc) */
- #define FD_NOUNIT 1 /* unit does not exist */
- #define FD_UNITBUSY 2 /* unit already active */
- #define FD_NOTACTIVE 3 /* unit is not active */
- #define FD_NOTREADY 4 /* unit is not ready (motor not on/no disk) */
-
- /*
- * Floppy ID values
- */
- #define FD_NODRIVE 0x00000000 /* response when no unit is present */
- #define FD_HD_3 0xaaaaaaaa /* high-density 3.5'' (1760K) drive */
- #define FD_DD_3 0xbbbbbbbb /* high-density 3.5'' (880K) drive */
-
-
- /* Atari: Auto probing not implemented yet! If CONFIG_FLOPPY_DD_ONLY
- * is not defined, all accesses are done for a HD disk.
- */
-
- /* For Atari, fields rdsz, wrsz, sm, pc1, pc2, sd, st, st unused */
-
- static struct fd_drive_type drive_types[] = {
- /* code name tr he rdsz wrsz sm pc1 pc2 sd st st*/
- { FD_HD_3, "HD 3.5", 160, 2, 25000, 25000, 2, 80,161, 3000, 18000, 1000},
- { FD_DD_3, "DD 3.5", 160, 2, 25000, 25000, 1, 80,161, 3000, 18000, 1000},
- { FD_NODRIVE, "No Drive", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
- };
- #ifdef __notneeded__
- static int num_dr_types = sizeof(drive_types) / sizeof(drive_types[0]);
- #endif
-
- static struct fd_data_type data_types[] = {
- { "Atari", 9 }
- };
- #ifdef __notneeded__
- static int num_da_types = sizeof(data_types) / sizeof(data_types[0]);
- #endif
-
-
- /* current info on each unit */
- static struct floppy_struct unit[FD_MAX_UNITS];
-
- /* track buffer */
- int savedtrack = -1;
- unsigned char trackdata[512];
-
- /*
- * These are global variables, as that's the easiest way to give
- * information to interrupts. They are the data used for the current
- * request.
- */
- char block_flag = 0;
- int selected = 0;
- struct wait_queue *wait_on_floppy_select = NULL;
- struct wait_queue *wait_fd_block = NULL;
-
- /* Synchronization of FDC access. */
- #ifdef __notneeded__
- static volatile int fdc_busy = 0;
- static struct wait_queue *fdc_wait = NULL;
- static struct wait_queue *motor_wait = NULL;
- #endif
-
- #if 0
- static unsigned int changed_floppies = 0;
- #endif
-
- /*======================================================================
- Select the side to use for a particular drive.
- The drive must have been calibrated at some point before this.
- The drive must also be active and the motor must be running.
- ======================================================================*/
- #ifdef __notneeded__
- static void fd_select_side(int drive, int side)
- {
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if(side==0)
- {
- sound_ym.rd_data_reg_sel=14; /* Select PSG Port A */
- sound_ym.wd_data = sound_ym.rd_data_reg_sel | 0x01;
- }
- else
- {
- sound_ym.rd_data_reg_sel=14; /* Select PSG Port A */
- sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xfe;
- }
-
- restore_flags(flags);
- }
- #endif
-
-
- #ifdef __notneeded__
- static struct timer_list motor_on_timer;
- static struct timer_list motor_off_timer[FD_MAX_UNITS];
- static int on_attempts;
- #endif
-
- static void wait_func(void)
- {
- int i;
- for(i=0;i<30;i++)
- i=i;
- }
-
- static void fd_select (int drive,unsigned short tr)
- {
- int oldtr;
- unsigned long flags;
- unsigned char tmp;
-
- if (drive == selected)
- return;
- oldtr=tr/2;
- selected = drive;
-
- save_flags(flags);
- cli();
-
- if(oldtr*2==tr)
- {
- sound_ym.rd_data_reg_sel=14; /* Select PSG Port A */
- tmp = sound_ym.rd_data_reg_sel;
- sound_ym.wd_data = tmp & ~2; /* Select Side 0 and Drive A */
- }
- else
- {
- sound_ym.rd_data_reg_sel=14; /* Select PSG Port A */
- tmp = sound_ym.rd_data_reg_sel;
- sound_ym.wd_data = tmp & ~3; /* Select Side 1 and Drive A */
- }
-
- restore_flags(flags);
- }
-
- static void fd_deselect (int drive)
- {
- unsigned long flags;
-
- if (drive != selected)
- return;
-
- save_flags(flags);
- cli();
- sound_ym.rd_data_reg_sel=14; /* Select PSG Port A */
- sound_ym.wd_data = sound_ym.rd_data_reg_sel | 7; /* no drives selected */
- restore_flags(flags);
-
- selected=-1;
- }
-
- static void fd_nobusy(void)
- {
- unsigned char fd_status;
-
- do
- fd_status = mfp.par_dt_reg & 0x20;
- while(fd_status != 0);
-
- }
-
- /*======================================================================
- Seek the drive to track 0.
- The drive must be active and the motor must be running.
- Returns standard floppy error code.
- ======================================================================*/
- #ifdef __notneeded__
- static int fd_calibrate(int drive)
- {
-
- fd_select(drive,0);
- dma_wd.dma_mode_status=FDCSELREG_STP;
- dma_wd.fdc_acces_seccount=FDCCMD_RESTORE | FDCCMDADD_V ;
-
- fd_nobusy();
- fd_deselect(drive);
-
- return 1;
- }
- #endif
-
- /*======================================================================
- Seek the drive to the requested cylinder.
- The drive must have been calibrated at some point before this.
- The drive must also be active and the motor must be running.
- ======================================================================*/
- static int fd_seek(unsigned short drive, unsigned short track)
- {
- dma_wd.dma_mode_status=FDCSELREG_DTA;
- nop();
- dma_wd.fdc_acces_seccount=track;
- wait_func();
- dma_wd.dma_mode_status=FDCSELREG_STP;
- nop();
- dma_wd.fdc_acces_seccount=FDCCMD_SEEK | FDCCMDADD_V ;
- wait_func();
- fd_nobusy();
-
- return 1;
- }
-
-
- struct header {
- unsigned char magic;
- unsigned char track;
- unsigned char sect;
- unsigned char ord;
- unsigned char labels[16];
- unsigned long hdrchk;
- unsigned long datachk;
- };
-
-
-
- /*==========================================================================
- atari_write converts track/labels data to raw track data
- ==========================================================================*/
- static void atari_write(int dr, unsigned short tr, unsigned short sec)
- {
- unsigned long phys_secbuf,flags;
- int i;
-
- sec=sec+1;
-
- phys_secbuf=VTOP((unsigned long)trackdata); /* DMA knows PhysAdr only ! */
-
- save_flags(flags);
- cli();
-
- fd_select(dr,tr);
-
- tr=tr/2;
-
- /* Setup head pos. */
- fd_seek(dr,tr);
-
-
- /* Setup DMA */
- dma_wd.dma_lo=(unsigned char)phys_secbuf;
- phys_secbuf = phys_secbuf >> 8;
- dma_wd.dma_md=(unsigned char)phys_secbuf;
- phys_secbuf = phys_secbuf >> 8;
- dma_wd.dma_hi=(unsigned char)phys_secbuf;
-
-
- /* Clear FIFO and switch DMA to read-mode ! */
- dma_wd.dma_mode_status=0x190;
- dma_wd.dma_mode_status=0x90;
- dma_wd.dma_mode_status=0x190;
-
- /* Transmit only 1 Sector */
- dma_wd.fdc_acces_seccount=0x01;
- wait_func();
-
-
- dma_wd.dma_mode_status=FDCSELREG_SEC | 0x100 ;
- nop();
- dma_wd.fdc_acces_seccount=sec;
- wait_func();
-
- /* Start Write */
- dma_wd.dma_mode_status=FDCSELREG_STP | 0x100 ;
- nop();
- dma_wd.fdc_acces_seccount=FDCCMD_WRSEC ;
- wait_func();;
-
- fd_nobusy(); /* Wait for IRQ 5 */
-
- restore_flags(flags);
-
- fd_deselect(dr);
-
- for(i=0;i<20000;i++)
- {
- i=i;
- }
- }
-
- /*==========================================================================
- atari_read reads a raw track of data into a track buffer
- ==========================================================================*/
- static int atari_read(int dr, unsigned short tr, unsigned short sec)
- {
- unsigned long phys_secbuf,flags;
-
- phys_secbuf=VTOP((unsigned long)trackdata);
-
- sec=sec+1;
-
- save_flags(flags);
- cli();
-
- fd_select(dr,tr);
-
- tr=tr/2;
-
- /* Setup head pos. */
- fd_seek(dr,tr);
-
-
- /* Setup DMA */
- dma_wd.dma_lo=(unsigned char)phys_secbuf;
- phys_secbuf = phys_secbuf >> 8;
- dma_wd.dma_md=(unsigned char)phys_secbuf;
- phys_secbuf = phys_secbuf >> 8;
- dma_wd.dma_hi=(unsigned char)phys_secbuf;
-
-
- /* Clear FIFO and switch DMA to read-mode ! */
- dma_wd.dma_mode_status=0x90;
- dma_wd.dma_mode_status=0x190;
- dma_wd.dma_mode_status=0x90;
- /* Transmit only 1 Sector */
- dma_wd.fdc_acces_seccount=0x01;
- wait_func();
-
- dma_wd.dma_mode_status=FDCSELREG_SEC;
- nop();
- dma_wd.fdc_acces_seccount=sec;
- wait_func();
-
- /* Start READ */
- dma_wd.dma_mode_status=FDCSELREG_STP;
- nop();
- dma_wd.fdc_acces_seccount=FDCCMD_RDSEC ;
- wait_func();
-
- fd_nobusy(); /* Wait for IRQ 5 */
-
- restore_flags(flags);
-
- fd_deselect(dr);
-
- return 0;
- }
-
- /*
- * Note that MAX_ERRORS=X doesn't imply that we retry every bad read
- * max X times - some types of errors increase the errorcount by 2 or
- * even 3, so we might actually retry only X/2 times before giving up.
- */
- #define MAX_ERRORS 12
-
- /*
- * The driver is trying to determine the correct media format
- * while probing is set. rw_interrupt() clears it after a
- * successful access.
- */
- static int probing = 0;
-
- /* Prevent "aliased" accesses. */
- static fd_ref[4] = { 0,0,0,0 };
- static fd_device[4] = { 0,0,0,0 };
-
- /*
- * Current device number. Taken either from the block header or from the
- * format request descriptor.
- */
- #define CURRENT_DEVICE (CURRENT->dev)
-
- /* Current error count. */
- #define CURRENT_ERRORS (CURRENT->errors)
-
- static void floppy_off (unsigned int nr)
- {
- }
-
-
-
- /*
- * floppy-change is never called from an interrupt, so we can relax a bit
- * here, sleep etc. Note that floppy-on tries to set current_DOR to point
- * to the desired drive, but it will probably not survive the sleep if
- * several floppies are used at the same time: thus the loop.
- */
- int floppy_change(struct buffer_head * bh)
- {
- return 0;
- }
-
- static __inline__ void copy_buffer(void *from, void *to)
- {
- ulong *p1,*p2;
- int cnt;
-
- p1 = (ulong *)from;
- p2 = (ulong *)to;
-
- for (cnt = 0; cnt < 512/4; cnt++)
- *p2++ = *p1++;
- }
-
-
- static void redo_fd_request(void)
- {
- unsigned int block, track, sector;
- int device, drive, cnt;
- struct floppy_struct *floppy;
- char *data;
-
-
- if (CURRENT && CURRENT->dev < 0) return;
-
- repeat:
-
- if(!CURRENT)
- return;
-
- if (MAJOR(CURRENT->dev) != MAJOR_NR)
- panic(DEVICE_NAME ": request list destroyed");
-
- if (CURRENT->bh) {
- if (!CURRENT->bh->b_lock)
- panic(DEVICE_NAME ": block not locked");
- }
-
- probing = 0;
- device = MINOR(CURRENT_DEVICE);
-
- if (CURRENT_DEVICE == 0x200 ) {
- /* manual selection */
- drive = 0;
- floppy = unit + drive;
- }
- else
- {
- printk("Unknown Device !\n");
- return;
- }
-
- for (cnt = 0; cnt < 2; cnt++) {
- block = CURRENT->sector + cnt;
- if ((int)block > floppy->blocks) {
- end_request(0);
- goto repeat;
- }
-
- track = block / floppy->dtype->sects;
- sector = block - track * floppy->dtype->sects;
- data = CURRENT->buffer + 512 * cnt;
-
-
- switch (CURRENT->cmd) {
- case READ:
- atari_read(device,track,sector);
- copy_buffer(trackdata,data);
- break;
-
- case WRITE:
- copy_buffer(data,trackdata);
- atari_write(device,track,sector);
- break;
-
- default:
- printk("do_fd_request: unknown command\n");
- end_request(0);
- goto repeat;
- }
- }
-
- end_request(1);
- goto repeat;
- }
-
- void do_fd_request(void)
- {
- redo_fd_request();
- }
-
- static int fd_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long param)
- {
- printk("fd_ioctl not yet implemented: %d\n", cmd);
- return -EINVAL;
- }
-
- /*======================================================================
- Return unit ID number of given disk
- ======================================================================*/
- #ifdef __notneeded__
- static unsigned long get_drive_id(int drive)
- {
- return FD_HD_3;
- }
- #endif
-
- static void fd_probe(int drive)
- {
- unit[drive].type = NULL;
- if (drive != 0)
- /* At the moment, only one floppy drive is supported */
- return;
-
- #ifdef CONFIG_FLOPPY_DD_ONLY
- unit[drive].type = &drive_types[1]; /* DD */
- #else
- unit[drive].type = &drive_types[0]; /* HD */
- #endif
-
- unit[drive].dtype = &data_types[0]; /* only one type */
- unit[drive].track = -1;
-
- unit[drive].sects = unit[drive].dtype->sects * unit[drive].type->sect_mult;
- unit[drive].blocks = unit[drive].type->heads * unit[drive].type->tracks *
- unit[drive].sects;
-
- unit[drive].disk = -1;
- unit[drive].motor = 0;
- unit[drive].busy = 0;
- unit[drive].status = -1;
- }
-
- static void config_types(void)
- {
- int drive;
-
- printk("Probing floppy drive(s):\n");
- for (drive = 0; drive < FD_MAX_UNITS; drive++) {
- fd_probe(drive);
- if (unit[drive].type != NULL)
- printk("%d: %s\n", drive, unit[drive].type->name);
- }
- }
-
- /*
- * floppy_open check for aliasing (/dev/fd0 can be the same as
- * /dev/PS0 etc), and disallows simultaneous access to the same
- * drive with different device numbers.
- */
- static int floppy_open(struct inode *inode, struct file *filp)
- {
- int drive;
- int old_dev;
-
- drive = inode->i_rdev & 3;
- old_dev = fd_device[drive];
-
- if (fd_ref[drive])
- if (old_dev != inode->i_rdev)
- return -EBUSY;
-
- fd_ref[drive]++;
- fd_device[drive] = inode->i_rdev;
-
- if (old_dev && old_dev != inode->i_rdev)
- invalidate_buffers(old_dev);
-
- if (filp && filp->f_mode)
- check_disk_change(inode->i_rdev);
-
-
- return 0;
- }
-
- static void floppy_release(struct inode * inode, struct file * filp)
- {
- sync_dev(inode->i_rdev);
- if (!fd_ref[inode->i_rdev & 3]--) {
- printk("floppy_release with fd_ref == 0");
- fd_ref[inode->i_rdev & 3] = 0;
- }
- }
-
- static struct file_operations floppy_fops = {
- NULL, /* lseek - default */
- block_read, /* read - general block-dev read */
- block_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- fd_ioctl, /* ioctl */
- NULL, /* mmap */
- floppy_open, /* open */
- floppy_release, /* release */
- block_fsync /* fsync */
- };
-
- #ifdef __notneeded__
- static void fd_block_done(struct intframe *fp, void *data)
- {
- }
- #endif
-
- void floppy_init(void)
- {
- int i;
-
- if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
- printk("Unable to get major %d for floppy\n",MAJOR_NR);
- return;
- }
- /* initialize variables */
- selected = -1;
-
- for (i = 0; i < FD_MAX_UNITS; i++) {
- unit[i].track = -1;
- }
-
- /* blk_size[MAJOR_NR] = floppy_sizes; */
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-
- config_types();
- }
-